#include "gen_reader.h"


/* default constructor
*/
gen_reader::gen_reader()
{
}

/* 
 *	initialize gen_reader 
 *	open bin file and access mdb for the generator variable setting
 */
gen_reader::gen_reader(char *HourlyBinFile, char *SummaryBinFile, char *dbFile)
	: reader(HourlyBinFile, dbFile)
{
	summary_file_Handle = open(SummaryBinFile, O_BINARY);
	if (summary_file_Handle >0) 
	{
		printf ("Open %s succeeded.\n", SummaryBinFile);
	}
	else 
	{
		printf ("Failed Open %s.\n", SummaryBinFile);
		switch (errno) 
		{
			case EACCES:  printf ("Permission denied.\n"); break;
			case EMFILE:  printf ("No file handle available.\n"); break;
			case ENOENT:  printf ("File or path not found.\n"); break;
			default:      printf ("Unknown error.\n"); break;
		}
	}

	try
	{
		CRecordset rs(&db);
		/* READ GEN Ancillary Service Type Index */
		rs.Open(CRecordset::forwardOnly, _T(GET_GEN_ANC_INDEX_SQL));
		rs.GetFieldValue("VALUE", gen_anc_setting);
		readAncSetting(gen_anc_setting);
		rs.Close();

		/* READ NUMBER OF EMISSION */
		rs.Open(CRecordset::forwardOnly, _T(GET_GEN_EMISSION_COUNT_SQL));
		rs.GetFieldValue("NUM_EMI", gen_emssions_count);
		readEmssionsCount(gen_emssions_count);
		rs.Close();
		
		/* READ EMISSION OPTION FROM DB, Yanli Wei, 06/09/2010*/
		rs.Open(CRecordset::forwardOnly, _T(GET_GEN_EMISSION_SETTING_SQL));
		rs.GetFieldValue("VALUE", gen_emssions_option);
		readEmssionsOption(gen_emssions_option);
		rs.Close();
		
		/* READ EMISSION TYPES FROM DB, Yanli Wei, 06/09/2010*/
        readEmissionType();
		
		/* READ GEN VARIABLE SETTING*/
		rs.Open(CRecordset::forwardOnly, _T(GET_GEN_VAR_SQL));
		rs.GetFieldValue("VALUE", gen_var_setting);
		num_vars = 0;
		read_gen_var_setting(&gen_var_setting);
		rs.Close();
	
		/* READ GEN TYPES FROM DB*/
		setGenTypeMap();
        
		/* READ GEN SUBTYPES FROM DB*, Yanli Wei, 06/04/2010*/
		readGenSubtype();
		
		/* READ TOTAL NUMBER OF GENERATOR FROM Hourly BINARY File*/
		read_gen_num(file_Handle, &hourly_num_gen);

		/* READ ALL GENERATOR INFO FROM Hourly BINARY File*/
		read_gen_info(file_Handle, &hourly_num_gen, &hourly_gen_info_list, &hourly_gen_index_map);
	}
	catch (CDBException *p)
	{
		printf("%s\n", p->m_strError);
	}

}
/*
 *	read number of generators from binary file
 */
void gen_reader:: read_gen_num(int f_handle, long *num)
{
	lseek(file_Handle, NUM_GEN_OFFSET, SEEK_SET);
	read(file_Handle, num, sizeof(num));
	cout<<"total "<<*num<<endl;
}

/*
 *	return number of generators
 */
long gen_reader::get_num_gen() 
{
	return hourly_num_gen;
}

/*
 *	read ancillary service setting, Yanli Wei, 06/10/2010
 */
void gen_reader::readAncSetting(CString ancIndex)
{
	num_ancs=0;
	int i, j;
	for (i=0; i< (ancIndex.GetLength()); i++)
	{	
		_stscanf(ancIndex.Mid (i, 1), _T("%d"), &j);
		switch (i)
			{
				case 0:		if (j) {
							AS_type_list.push_back("Regulation Down");
							num_ancs++;
							}
							break;
				case 1:		if (j) {
							AS_type_list.push_back("Regulation Up");
							num_ancs++;;
							}
							break;
				case 2:		if (j) {
							AS_type_list.push_back("Spinning Reserve");
							num_ancs++;;
							}
							break;
				case 3:		if (j) {
							AS_type_list.push_back("Non-Spinning Reserve");
							num_ancs++;;
							}
							break;
				case 4:		if (j) {
							AS_type_list.push_back("Replacement Reserve");
							num_ancs++;;
							}
							break;
		}
	}
}

/*
 *	return emissions option, Yanli Wei, 06/09/2010
 */
void gen_reader::readEmssionsOption(CString gen_emssions_option)
{
		emissions_option = atoi(gen_emssions_option); 
}

/*
 *	return number of emissions, Yanli Wei, 06/09/2010
 */
void gen_reader::readEmssionsCount(CString gen_emssions_count)
{
		num_emissions = atoi(gen_emssions_count);

}

/*
 *  Read emission types, Yanli Wei, 06/09/2010
 */
void gen_reader::readEmissionType()
{
	CString emissionType;
	CRecordset rs(&db);
	rs.Open(CRecordset::forwardOnly, _T(GET_GEN_EMISSION_NAME_SQL));
	while ( !rs.IsEOF() ) 
	{
		rs.GetFieldValue("NAME", emissionType);
		emission_type_list.push_back(emissionType);
		rs.MoveNext();
	}
	rs.Close();
}

/*
 *	read current generators output variable setting
 *	and store setting into a string vector:gen_var_list
 */
void gen_reader::read_gen_var_setting (CString * gen_var_setting)
{
	CString output;
	int i=0, j=0;
		for (i=0; i< (gen_var_setting->GetLength()); i++)
		{
			_stscanf(gen_var_setting->Mid (i, 1), _T("%d"), &j);
			switch (i)
			{
				case 0:		if (j) {
							gen_var_list.push_back("LMP ($/MWh)");
							var_index_map.insert(make_pair("LMP ($/MWh)", ++num_vars));
							}
							break;

				case 1:		if (j) {
							gen_var_list.push_back("LMP - Congestion ($/MWh)");
							var_index_map.insert(make_pair("LMP - Congestion ($/MWh)", ++num_vars));
							}
							break;

				case 2:		if (j) {
							gen_var_list.push_back("LMP - Loss ($/MWh)");	
							var_index_map.insert(make_pair("LMP - Loss ($/MWh)", ++num_vars));
							}
							break;

				case 3:		if (j) {
							gen_var_list.push_back("Capacity (MW)");	
							var_index_map.insert(make_pair("Capacity (MW)", ++num_vars));
							}
							break;

				case 4:		if (j) {
							gen_var_list.push_back("Generation (MWh)");
							var_index_map.insert(make_pair("Generation (MWh)", ++num_vars));
							}
							break;

				case 5:		if (j) {
							gen_var_list.push_back("Total Cost ($)");
							var_index_map.insert(make_pair("Total Cost ($)", ++num_vars));
							}
							break;

				case 6:		if (j) {
							gen_var_list.push_back("Revenue ($)");
							var_index_map.insert(make_pair("Revenue ($)", ++num_vars));
							}
							break;

				case 7:		if (j) {
							gen_var_list.push_back("Net Revenue ($)");
							var_index_map.insert(make_pair("Net Revenue ($)", ++num_vars));
							}
							break;

				case 8:		if (j) {
							gen_var_list.push_back("Maintenance");
							var_index_map.insert(make_pair("Maintenance", ++num_vars));
							}
							break;

				case 9:		if (j) {
							gen_var_list.push_back("Forced Outage");
							var_index_map.insert(make_pair("Forced Outage", ++num_vars));
							}
							break;

				case 10:	if (j) {
							gen_var_list.push_back("Commitment");
							var_index_map.insert(make_pair("Commitment", ++num_vars));
							}
							break;

				case 11:	if (j) {
							gen_var_list.push_back("Full Load Average Cost ($/MWh)");
							var_index_map.insert(make_pair("Full Load Average Cost ($/MWh)", ++num_vars));
							}
							break;
							
				case 12:	if (j) {
							gen_var_list.push_back("Variable O&&M Cost ($)");
							var_index_map.insert(make_pair("Variable O&&M Cost ($)", ++num_vars));
							}
							break;

				case 13:	if (j) {
							gen_var_list.push_back("fuel Cost ($)");
							var_index_map.insert(make_pair("fuel Cost ($)", ++num_vars));
							}
							break;

				case 14:	if (j) {
							gen_var_list.push_back("Price Markup Ratio");
							var_index_map.insert(make_pair("Price Markup Ratio", ++num_vars));
							}
							break;

				case 15:	if (j) {
							gen_var_list.push_back("Delivery Factor");
							var_index_map.insert(make_pair("Delivery Factor", ++num_vars));
							}
							break;
							
				case 16:	if (j) {
							gen_var_list.push_back("Primary Fuel Consumption");
							var_index_map.insert(make_pair("Primary Fuel Consumption", ++num_vars));
							}
							break;

				case 17:	if (j) {
							gen_var_list.push_back("Secondary Fuel Consumption");
							var_index_map.insert(make_pair("Secondary Fuel Consumption", ++num_vars));
							}
							break;

				case 18:	if (j) {
							gen_var_list.push_back("Primary Fuel Generation");
							var_index_map.insert(make_pair("Primary Fuel Generation", ++num_vars));
							}
							break;
							
				case 19:	if (j) {
							gen_var_list.push_back("Secondary Fuel Generation");
							var_index_map.insert(make_pair("Secondary Fuel Generation", ++num_vars));
							}
							break;

				case 20:	if (j) {
							gen_var_list.push_back("Marginal Unit");
							var_index_map.insert(make_pair("Marginal Unit", ++num_vars));
							}
							break;
				// Emission Amount by type, Yanli Wei, 06/09/2010
				case 21:	if (j) {
							if (emissions_option == 0)
								break;
							else 
								for ( int i=1; i<num_emissions+1; i++)
								{
									output = emission_type_list[i-1]+" Amount";
									gen_var_list.push_back((char*)(LPCTSTR)output);
									var_index_map.insert(make_pair(output, ++num_vars));
								}
							}
							break;
				// Emission Cost by type, Yanli Wei, 06/09/2010
				case 22:	if (j) {
							if (emissions_option == 0)
								break;
							else 
								for ( int i=1; i<num_emissions+1; i++)
								{
									output = emission_type_list[i-1]+" Cost";
									gen_var_list.push_back((char*)(LPCTSTR)output);
									var_index_map.insert(make_pair(output, ++num_vars));
								}
							}
							break;
				// AS Served Amount, Yanli Wei, 06/10/2010			
				case 23:	if (j) {
							if (num_ancs == 0)
								break;
							else 
								for ( int i=1; i<num_ancs+1; i++)
								{
									output = AS_type_list[i-1] + " Served Amount";
									gen_var_list.push_back((char*)(LPCTSTR)output);
									var_index_map.insert(make_pair(output, ++num_vars));
								}
							}
							break;
				// AS Price, Yanli Wei, 06/10/2010
				case 24:	if (j) {
							if (num_ancs == 0)
								break;
							else 
								for ( int i=1; i<num_ancs+1; i++)
								{
									output = AS_type_list[i-1] + " Price";
									gen_var_list.push_back((char*)(LPCTSTR)output);
									var_index_map.insert(make_pair(output, ++num_vars));
								}
							}
							break;
				// AS Revenue, Yanli Wei, 06/10/2010				
				case 25:	if (j) {
							if (num_ancs == 0)
								break;
							else 
								for ( int i=1; i<num_ancs+1; i++)
								{
									output = AS_type_list[i-1] + " Revenue";
									gen_var_list.push_back((char*)(LPCTSTR)output);
									var_index_map.insert(make_pair(output, ++num_vars));
								}
							}
							break;
			}

		}
		num_vars = var_index_map.size();
}

/*
 *	return generator variable list
 */
char ** gen_reader::get_gen_var_list()
{
	return &gen_var_list.front();
}

/*
 *	return generator variable list size
 */
int gen_reader::get_var_list_size()
{
	return gen_var_list.size();
}

/*
 *	read generator info from binary file
 */

void gen_reader::read_gen_info(int f_handler, long * num, vector<gen_info> * info_list, map <CString, int> * info_map)
{
	gen_info generator;
	for (int i=0; i< *num; i++)
	{
		lseek(f_handler, i*GEN_INFO_HEADER_SIZE + GEN_INFO_HEADER_OFFSET, SEEK_SET);
		read(f_handler, &generator, sizeof(generator));
		info_list->push_back(generator);
		// Added to avoid wrong "gen_id" output once there are 2 bytes information written into bin file, Yanli Wei, 06/15/2010
		(*info_list)[i].gen_id[2] = '\0'; 
		//cout<<(*info_list)[i].gen_name << endl << (*info_list)[i].bus_id << endl;
		//cout<<(*info_list)[i].gen_id<<endl;
		info_map->insert(make_pair(CString(generator.gen_name), i+1));
	}

}

/*
 *	return generator info list
 */

gen_info * gen_reader::get_gen_info_list ()
{
	return &hourly_gen_info_list.front();
}

/*
 *	return generator info list size
 */

int gen_reader::get_gen_info_list_size()
{
	return hourly_gen_info_list.size();
}

/*
 *	return generator var list size
 */

int gen_reader::get_gen_var_list_size()
{
	return num_vars;
}

/*
 * return lookup generator index by its name
 */

int gen_reader::get_gen_index(char * gen_name, map <CString, int> * info_map)
{
	return (*info_map)[CString(gen_name)];
}

/*
 * return binary file offset 
 * input generator index, which hour and variable index
 */
unsigned long int gen_reader::get_hourly_var_offset(int gen_index, unsigned long gen_hrs, int var_index)
{

	if (gen_index==0 || gen_hrs==0 || var_index ==0)
		return -1;
	int size_gen_var = num_vars * sizeof (float);
	int gen_hourly_start_offset = GEN_INFO_HEADER_OFFSET + hourly_num_gen * GEN_INFO_HEADER_SIZE;	
	unsigned long int gen_hourly_offset = size_gen_var*hourly_num_gen*(gen_hrs-1) + (gen_index-1)*size_gen_var + REF_PRICE_SIZE*gen_hrs;
	int var_offset = (var_index-1)*sizeof(float);
	return gen_hourly_start_offset + gen_hourly_offset + var_offset;
}

float gen_reader::get_gen_var(char *gen_name, char *var_name, int gen_hrs)
{
		int gen_index = hourly_gen_index_map[CString(gen_name)];
		int var_index = var_index_map[CString(var_name)];
		if ( gen_index == 0 || var_index == 0)
			return -1;
		unsigned long offsetVal = get_hourly_var_offset(gen_index, gen_hrs, var_index);
		return get_offset_value(offsetVal);
}


float * gen_reader::getAllHourlyVal(char* gen_name, unsigned long gen_hrs)
{
	float * val;
	val = (float*)0;
	int gen_index = hourly_gen_index_map[gen_name];
	int var_index = var_index_map[gen_var_list[0]];
	if ( gen_index == 0 || var_index == 0) /* Added to check generator name and variable name validity*/ 
		return (float*)0;                  /* Revised by Yanli Wei, 06/02/2010 */
	unsigned long offsetVal = get_hourly_var_offset(gen_index, gen_hrs, var_index);
	val = (float*) calloc(num_vars, sizeof(float));
	lseek(file_Handle, offsetVal, SEEK_SET);
	read(file_Handle, val, sizeof(float)*num_vars);
	return val;
}

vector<CString> gen_reader::get_gen_area_list()
{
	CRecordset rs(&db);
	vector<CString> area_list;
	CString area;
	if (saveBinAll == 0)
		rs.Open(CRecordset::forwardOnly, _T(GET_GEN_AREA_SEL_SQL));
	else 
		rs.Open(CRecordset::forwardOnly, _T(GET_GEN_AREA_ALL_SQL));
	int size = rs.GetRecordCount();
	while ( !rs.IsEOF() ) 
	{
		rs.GetFieldValue("LoadAreaName", area);
		area_list.push_back(area);
		rs.MoveNext();
	}
	rs.Close();
	return area_list;
}

vector<CString> gen_reader::get_gen_list_byTypeAndArea(CString type, CString Area)
{
	CString type_sql("");
	CString area_sql("");
	CString saveBinary_sql("");
	CString value;
	CString query("SELECT GENERATORNAME FROM GENERATOR");
	vector<CString> gen_names;
	int where_flag =0;
	int and_flag = -1;
	if (type.MakeLower() != "all")
	{
		value.Format("%d", gen_type_map[type]);
		if (gen_type_map[type]==0)
			return gen_names;
		type_sql = "GENERATORTYPE =" + value;
		where_flag = 1;
		and_flag++;
	}
	if (Area !="all")
	{
		area_sql= "LOADAREAID = (SELECT LOADAREAID FROM LOADAREA WHERE LOADAREANAME='" + Area +"')";
		where_flag = 1;
		and_flag++;
	}
	if ( saveBinAll == 0)
	{
		saveBinary_sql = "SAVETOBINARY = -1";
		where_flag = 1;
		and_flag++;
	}
	if ( where_flag == 1)
	{
		query = query + " WHERE ";
		if (and_flag ==2)
			query = query + type_sql + " AND " + area_sql + " AND " + saveBinary_sql;
		else if (and_flag ==1)
		{
			if ( type_sql.GetLength() !=0)
				query = query + type_sql + " AND " + area_sql + saveBinary_sql;
			else
				query = query + area_sql + " AND " + saveBinary_sql;
		}
		else 
		{
			query = query + type_sql + area_sql + saveBinary_sql;
		}
	}
	cout<<query<<endl;
	CString genName;
	CRecordset rs(&db);
	rs.Open(CRecordset::forwardOnly, query);

	while ( !rs.IsEOF() ) 
	{
		rs.GetFieldValue("GENERATORNAME", genName);
		gen_names.push_back(genName);
		rs.MoveNext();
	}
	rs.Close();
	return gen_names;
}

void gen_reader::setGenTypeMap()
{
	CString type;
	CRecordset rs(&db);
	rs.Open(CRecordset::forwardOnly, _T(GET_GEN_TYPES));
	int i;//, j;
	while ( !rs.IsEOF() ) 
	{
		rs.GetFieldValue("TYPE", type);
		i = atoi(type);
		switch (i) 
		{
			case 1:
				gen_type_map.insert(make_pair(CString("Thermal").MakeLower(), i));
				gen_type_list.push_back(CString("Thermal"));
				break;
			case 2:
				gen_type_map.insert(make_pair(CString("Hydro").MakeLower(), i));
				gen_type_list.push_back(CString("Hydro"));
				break;
			case 3:
				gen_type_map.insert(make_pair(CString("Pumped Storage").MakeLower(), i));
				gen_type_list.push_back(CString("Pumped Storage"));
				break;
			case 4:
				gen_type_map.insert(make_pair(CString("Hourly Resources").MakeLower(), i));
				gen_type_list.push_back(CString("Hourly Resources"));
				break;
			case 5:
				gen_type_map.insert(make_pair(CString("Energy Storage").MakeLower(), i));
				gen_type_list.push_back(CString("Energy Storage"));
				break;
			case 6:
				gen_type_map.insert(make_pair(CString("Dispatchable Load").MakeLower(), i));
				gen_type_list.push_back(CString("Dispatchable Load"));
				break;
			case 7:
				gen_type_map.insert(make_pair(CString("Demand Response").MakeLower(), i));
				gen_type_list.push_back(CString("Demand Response"));
				break;
			case 8:
				gen_type_map.insert(make_pair(CString("Compressed Air Energy Storage").MakeLower(), i));
				gen_type_list.push_back(CString("Compressed Air Energy Storage"));
				break;
		}
		rs.MoveNext();
	}
	rs.Close();
}

void gen_reader::readGenSubtype()
{
	CString subtype;
	CRecordset rs(&db);
	rs.Open(CRecordset::forwardOnly, _T(GET_GEN_SUBTYPES));
	while ( !rs.IsEOF() ) 
	{
		rs.GetFieldValue("SUBTYPE", subtype);
		gen_subtype_list.push_back(subtype);
		rs.MoveNext();
	}
	rs.Close();
}

gen_full_info gen_reader::get_gen_full_info(CString gen_name)
{
	CRecordset rs(&db);
	gen_full_info info; 
	CString query;
	query = GET_GEN_FULL_INFO + " and g.generatorname='" + gen_name +"'";
	cout<<query<<endl;
	rs.Open(CRecordset::forwardOnly, query);
	cout<<rs.IsEOF ()<<endl;
	if (!rs.IsEOF ())
	{
		rs.GetFieldValue("generatortype", info.gen_type);
		rs.GetFieldValue("subtype", info.gen_subtype); // Yanli Wei, 06/04/2010
		rs.GetFieldValue("loadareaname", info.area_name);
		rs.GetFieldValue("fuelname", info.fuel_name);
		rs.GetFieldValue("commissiondate", info.install_date);
		rs.GetFieldValue("retirementdate", info.retire_date);
		rs.GetFieldValue("regionname", info.region_name);
		rs.GetFieldValue("generatorname", info.gen_name);
	}
	rs.Close();
	//cout<<info.gen_name<<endl;
	//cout<<info.area_name<<endl;
	return info;
}

vector<gen_full_info> gen_reader::get_gen_full_info_all()
{
	CRecordset rs(&db);
	gen_full_info info={"","","","","","","",""};
	vector<gen_full_info> list;
	rs.Open(CRecordset::forwardOnly, _T(GET_GEN_FULL_INFO));
	while (!rs.IsEOF() )
	{
		rs.GetFieldValue("generatortype", info.gen_type);
		rs.GetFieldValue("subtype", info.gen_subtype); // Yanli Wei, 06/04/2010
		rs.GetFieldValue("loadareaname", info.area_name);
		rs.GetFieldValue("fuelname", info.fuel_name);
		rs.GetFieldValue("commissiondate", info.install_date);
		rs.GetFieldValue("retirementdate", info.retire_date);
		rs.GetFieldValue("regionname", info.region_name);
		rs.GetFieldValue("generatorname", info.gen_name);
		list.push_back(info);
		rs.MoveNext();
	}
	rs.Close();
	return list;
}

float gen_reader::getCommitCode(char * gen_name, int hour)
{
	float value = get_gen_var(gen_name, "Commitment", hour);
	return value;
}


unsigned long gen_reader::get_summary_data_offset(int gen_index)
{	
	cout<<"gen index "<<gen_index<<endl;
	if (gen_index==0)
		return -1;
	int size_summary_data = NUM_SUMMARY_DATA * sizeof (float);
	int summary_data_start_offset = GEN_INFO_HEADER_OFFSET + hourly_num_gen * GEN_INFO_HEADER_SIZE;
	return summary_data_start_offset + size_summary_data * (gen_index-1);
}

gen_yearly_data gen_reader::get_gen_yearly_data(CString gen_name)
{
	gen_yearly_data data={0,0,0,0,0,0,0,0,0,0,0,0};
	int offset = get_summary_data_offset(hourly_gen_index_map[gen_name]);
	cout<<offset<<endl;
	if ( offset == -1)
		return data;
	lseek(summary_file_Handle, offset, SEEK_SET);
	read(summary_file_Handle, &data, sizeof(data));	
	cout<<data.STAT_avg_LMP<<endl;
	cout<<data.STAT_avg_LMP_Cong<<endl;
	return data;
}

gen_reader::~gen_reader ()
{
	if (summary_file_Handle >0) 
	{
		if (close (summary_file_Handle) == 0)
		{
			printf ("Close yearly summary file succeeded.\n");
		}
		else 
		{
			printf ("Close yearly summary file failed.\n");
			switch (errno) 
			{
				case EBADF:  printf ("File not open.\n"); break;
				default:     printf ("Unknown error.\n"); break;
			}
		}
	}
}